#include "WindowClass.h"
#include "Utilities\Informant.h"
#include "Components\SettingsComponent.h"
#include "Components\MessageSystemComponent.h"

MessageSystemComponent* WindowClass::_MessageSystem = NULL;

WindowClass::WindowClass()
{
	_DeviceContextHandle = 0;
	_WindowHandle = 0;
	_IsFullScreen = false;
	_Window.left = _Window.top = _Window.right = _Window.bottom = 0;
}

WindowClass::WindowClass(int WindowWidth, int WindowHeight, bool FullScreen)
{
	_DeviceContextHandle = 0;
	_WindowHandle = 0;
	_IsFullScreen = FullScreen;
	_Window.left = _Window.top = 0;
	_Window.right = WindowWidth;
	_Window.bottom = WindowHeight;
}

WindowClass::~WindowClass()
{
	Shutdown();
}

void WindowClass::Initialize(HINSTANCE instance, MessageSystemComponent* MSC, SettingsComponent* SC)
{
	// Set the HINSTANCE
	_hInstance = instance;

	// Setup the default settings
	WNDCLASSEX wcex		= {0};
	wcex.cbSize			= sizeof(WNDCLASSEX);
	wcex.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wcex.lpfnWndProc	= (WNDPROC)WindowClass::_WinProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= _hInstance;
	wcex.hIcon			= LoadIcon(NULL, IDI_WINLOGO);
	wcex.hCursor		= (HCURSOR)LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)GetStockObject(BLACK_BRUSH);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= (LPCSTR)SC->GetClassName();
	wcex.hIconSm		= wcex.hIcon;

	// Register the class
	RegisterClassEx(&wcex);

	unsigned int uiStyle = 0;
	int Width = 0, Height = 0;
	int X = 0, Y = 0;

	SC->GetWindowDimensions(Width, Height);
	

	if(_IsFullScreen)
	{
		uiStyle = WS_POPUP | WS_VISIBLE | WS_MAXIMIZE;

		DEVMODE dmScreenSettings;
		memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
		dmScreenSettings.dmSize = sizeof(dmScreenSettings);
		dmScreenSettings.dmPelsWidth = Width;
		dmScreenSettings.dmPelsHeight = Height;
		dmScreenSettings.dmBitsPerPel = 32;
		dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

		ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);
	}
	else
	{
		uiStyle = WS_CAPTION | WS_SYSMENU;
		AdjustWindowRect(&_Window, uiStyle, FALSE);
		_Window.right -= _Window.left;
		_Window.bottom -= _Window.top;
		_Window.left = _Window.top = 0;

		X = (GetSystemMetrics(SM_CXSCREEN) - Width) /2;
		Y = (GetSystemMetrics(SM_CYSCREEN) - Height) /2;
	}

	
	const char* CLASS = SC->GetClassName();
	const char* WINNAME = SC->GetAppName();
	_Settings = SC;

	// Create the window
	_WindowHandle = CreateWindow((LPCSTR)CLASS, (LPCSTR)WINNAME, uiStyle, X, Y, Width, Height, NULL, NULL, wcex.hInstance, NULL);
	if(_WindowHandle != NULL)
	{
#if _DEBUG
		AllocConsole();
	//AttachConsole(GetCurrentProcessId());

	FILE *ptr = stdout;	// For the console
	freopen_s(&ptr, "CON", "w", stdout);

	HANDLE outh = GetStdHandle(STD_OUTPUT_HANDLE);
	COORD bufferSize = {160, 51};
	SetConsoleScreenBufferSize(outh, bufferSize);
	SMALL_RECT consoleSize = { 0, 0, 159, 50 };
	SetConsoleWindowInfo(outh, TRUE, &consoleSize);
#endif // _DEBUG

		// Needs to be set before anything window related
		_MessageSystem = MSC;

		::ShowWindow(_WindowHandle, SW_SHOW);
		SetForegroundWindow(_WindowHandle);
		SetFocus(_WindowHandle);
		GetWindowRect(_WindowHandle, &_Window);

	}
}

void WindowClass::Update()
{
	MSG msg = {0};
	while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

}

void WindowClass::Shutdown()
{
	if(_DeviceContextHandle != NULL)
	{
		ReleaseDC(_WindowHandle, _DeviceContextHandle);
		_DeviceContextHandle = NULL;
	}

	if(_WindowHandle != NULL)
	{
		DestroyWindow(_WindowHandle);
		_WindowHandle = NULL;
	}

	UnregisterClass(_Settings->GetClassName(), _hInstance);
	_hInstance = NULL;

	if(_IsFullScreen)
		ChangeDisplaySettings(NULL, NULL);

	//_MessageSystem->AddMessage(MT_CLOSE);
	
#if _DEBUG
	FreeConsole();
#endif
}

bool WindowClass::Resize(int WindowWidth, int WindowHeight, bool Fullscreen)
{
	if(_WindowHandle == NULL)
		return false;

	::ShowWindow(_WindowHandle, SW_HIDE);
	unsigned int Style, exStyle;

	if(Fullscreen)
	{
		DEVMODE ScreenSettings;
		memset(&ScreenSettings, 0, sizeof(ScreenSettings));
		ScreenSettings.dmSize = sizeof(ScreenSettings);
		ScreenSettings.dmPelsWidth = WindowWidth;
		ScreenSettings.dmPelsHeight = WindowHeight;
		ScreenSettings.dmBitsPerPel = 32;
		ScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

		if(ChangeDisplaySettings(&ScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
		{
			Informant::Log(LOCATION, "There was a problem changing the resolution");
			return false;
		}

		exStyle = WS_EX_APPWINDOW;
		Style = WS_POPUP;

		_IsFullScreen = true;
	}
	else
	{
		if(_IsFullScreen)
			ChangeDisplaySettings(NULL, NULL);

		_IsFullScreen = false;

		exStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
		Style = WS_OVERLAPPEDWINDOW;
	}

	RECT lpr;
	memset(&lpr, 0, sizeof(RECT));
	lpr.right = WindowWidth;
	lpr.bottom = WindowHeight;
	AdjustWindowRectEx(&lpr, Style, FALSE, exStyle);
	lpr.right -= lpr.left;
	lpr.bottom -= lpr.top;
	lpr.left = lpr.top = 0;

	SetWindowLongPtr(_WindowHandle, GWL_STYLE, Style);
	SetWindowLongPtr(_WindowHandle, GWL_EXSTYLE, exStyle);

	SetWindowPos(_WindowHandle, HWND_TOP, lpr.left, lpr.top, lpr.right, lpr.bottom, SWP_FRAMECHANGED | SWP_NOACTIVATE);
	GetWindowRect(_WindowHandle, &_Window);

	_MessageSystem->AddMessage(MT_RESIZE);

	return true;
}

bool WindowClass::ToggleFullScreen()
{
	return Resize(_Window.right, _Window.bottom, !_IsFullScreen);
}

LRESULT CALLBACK WindowClass::_WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch(message)
	{
	case WM_CREATE:
		{
			break;
		}
	case WM_ACTIVATE:
		{
			//	gaining focus
			if (LOWORD(wParam) != WA_INACTIVE)
			{
				// unpause game code here
				_MessageSystem->AddMessage(MT_UNPAUSE);
				Informant::Log(LOCATION, "Game gained focus");
			}
			else // losing focus
			{
				// pause game code here
				_MessageSystem->AddMessage(MT_PAUSE);
				Informant::Log(LOCATION, "Game lost focus");
			}
			break;
		}
	case WM_SYSCOMMAND:
		{
			switch (wParam)
			{
			case SC_SCREENSAVE:
			case SC_MONITORPOWER:
				return 0;
			}
			break;
		}
	case WM_PAINT:
		{
			PAINTSTRUCT PS;
			BeginPaint(hWnd, &PS);
			EndPaint(hWnd, &PS);
			break;
		}
	case WM_DESTROY:
		{
			_MessageSystem->AddMessage(MT_CLOSE);
			PostQuitMessage(0);
			break;
		}
	case WM_CLOSE:
		{
			DestroyWindow(hWnd);
			break;
		}
	}
	//	Process any messages that we didn't take care of 
	return (DefWindowProc(hWnd, message, wParam, lParam));
}

bool WindowClass::IsAlreadyRunning(HINSTANCE hPrevInstance)
{
	const char* CLASS = _Settings->GetClassName();
	const char* WINNAME = _Settings->GetAppName();
	HWND hWnd = FindWindow(CLASS, WINNAME);

	// Second copy found
	if (hWnd)
	{
		//	If it was minimized
		if (IsIconic(hWnd))
			//	restore it
			ShowWindow(hWnd, SW_RESTORE);

		//	Bring it to the front
		SetForegroundWindow(hWnd);

		return true;
	}

	//	Only Instance
	return false;
}